package net.pulseproject.router.modules.useable;

import java.util.Collection;

import javax.sound.midi.ShortMessage;

import net.pulseproject.commons.midi.Receivable;
import net.pulseproject.commons.midi.Transmitable;
import net.pulseproject.commons.midi.entity.ControllerMessage;
import net.pulseproject.commons.util.MidiUtil;
import net.pulseproject.router.modules.BiDirectionalModule;
import net.pulseproject.router.modules.logic.beatstabilizer.BeatHistory;
import net.pulseproject.router.modules.logic.beatstabilizer.BeatStabilizer;
import net.pulseproject.router.modules.logic.beatstabilizer.BeatStabilizerConfig;
import net.pulseproject.router.modules.logic.beatstabilizer.BeatStabilizerDebuggerGUiApp;
import net.pulseproject.router.modules.logic.beatstabilizer.BeatStabilizer.BeatStabilizerListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * - we dont need to be in sync with real beat, important is just to keep up with the speed
 * - respect two things:
 *   1. keep public visibile speed changes smooth
 *   2. try to stick with real beat as good as possible (global min/max, local=timeframe min/max)
 */
public class BeatStabilizerModule extends BiDirectionalModule implements BeatStabilizerListener {
	
	static final Log LOG = LogFactory.getLog(BeatStabilizerModule.class);
	
	private final BeatStabilizerConfig stabilizerConfig;
	private final BeatHistory history;
	private BeatStabilizer stabilizer;
	
	private boolean readyToUseHistory = false;
	private long initTimestamp = -1;
	
	public BeatStabilizerModule(String id, Collection<Receivable> outgoingModules, BeatStabilizerConfig stabilizerConfig) {
		super(id, outgoingModules);
		this.stabilizerConfig = stabilizerConfig;
		this.history = new BeatHistory(stabilizerConfig.getHistoryTimeToLiveMs());
	}
	
	@Override
	public final void receive(final ShortMessage message, final Transmitable transmitable) {
		LOG.debug("receive(message="+MidiUtil.toString(message)+", transmitable="+transmitable+")");
		this.history.add();
		BeatStabilizerDebuggerGUiApp.debugBlinkRawReceive();
		
		if(this.readyToUseHistory == false) {
			LOG.debug("  very first invocation; just adding to history and storing initDuration");
			this.readyToUseHistory = true; // ensure at least two entries in history
			this.initTimestamp = System.currentTimeMillis();
			
		} else if(this.stabilizer == null) {
			//  listener, BeatHistory history, long globalMinMs, int globalMaxMs, double frameDeviationPercent
			this.stabilizer = new BeatStabilizer(this, this.history, this.stabilizerConfig);
			
			long initDuration = System.currentTimeMillis() - this.initTimestamp;
			this.stabilizer.start(initDuration);
		}
	}

	public void destroy() { // FIXME move to public API, as other modules probably need to be destroyed as well (stop threads)
		this.stabilizer.stop();
		this.stabilizer = null;
		this.readyToUseHistory = false;
	}
	
	@Override
	public String toString() {
		return "BeatStabilizerModule[]";
	}

	@Override
	public void onBeatSignal() {
		LOG.debug("onBeatSignal()");
		BeatStabilizerDebuggerGUiApp.debugBlinkSet(true);
		// FIXME use very first message and just clone that one (use it as a template)
		this.broadcastToOutgoingModules(new ControllerMessage(1, 2, 42).build());
	}
	
}
